﻿using System.Collections;
using System.Collections.Generic;

namespace UnityView
{
    public sealed class LinkedNode<T>
    {
        internal LinkedNode<T> Previous;
        internal LinkedNode<T> Next;
        public T Value;
        public int Index;

        public LinkedNode(int index)
        {
            Index = index;
        }

        public static void Connect(LinkedNode<T> prev, LinkedNode<T> next)
        {
            prev.Next = next;
            next.Previous = prev;
        }

        public void SetPrevious(LinkedNode<T> prev)
        {
            Previous = prev;
            prev.Next = this;
        }

        public void SetNext(LinkedNode<T> next)
        {
            Next = next;
            next.Previous = this;
        }
    }

    /// <summary>
    /// 一个首尾相连的链表
    /// 用户保证任何时刻只有一部分链表(head, tail)处于使用状态
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CircleLinkedList<T> : ICollection<T>
    {
        public LinkedNode<T> Head;
        public LinkedNode<T> Tail;
        public LinkedNode<T> CurrentNode;

        public CircleLinkedList(int capacity = 2)
        {
            Head = new LinkedNode<T>(0);
            _capacity = capacity;
            CurrentNode = Head;
            for (int i = 1; i < capacity; i++)
            {
                var node = new LinkedNode<T>(i);
                CurrentNode.SetNext(node);
                CurrentNode = node;
            }
            CurrentNode.SetNext(Head);
            CurrentNode = Head;
            Tail = Head;
        }

        #region 迭代相关

        public void Add(T item)
        {
            AddAfter(item);
        }

        public T AddBefore(T item)
        {
            CurrentNode = CurrentNode.Previous;
            T oldValue = CurrentNode.Value;
            CurrentNode.Value = item;
            return oldValue;
        }

        public T AddBefore(T item, LinkedNode<T> node)
        {
            LinkedNode<T> prev = node.Previous;
            T oldValue = prev.Value;
            prev.Value = item;
            return oldValue;
        }

        public T AddAfter(T item)
        {
            CurrentNode = CurrentNode.Next;
            T oldValue = CurrentNode.Value;
            CurrentNode.Value = item;
            return oldValue;
        }

        public T AddAfter(T item, LinkedNode<T> node)
        {
            LinkedNode<T> next = node.Next;
            T oldValue = next.Value;
            next.Value = item;
            return oldValue;
        }
        
        public void MoveToPrevious()
        {
            CurrentNode = CurrentNode.Previous;
        }

        public void MoveToNext()
        {
            CurrentNode = CurrentNode.Next;
        }

        public LinkedNode<T> MoveNode(LinkedNode<T> node, int step)
        {
            if (step >= 0)
            {
                for (int i = 0; i < step; i++)
                {
                    node = node.Next;
                }
            }
            else
            {
                for (int i = 0; i < -step; i++)
                {
                    node = node.Previous;
                }
            }
            return node;
        }

        public void MoveHead(int step)
        {
            Head = MoveNode(Head, step);
        }

        public void MoveTail(int step)
        {
            Tail = MoveNode(Tail, step);
        }

        public void Clear()
        {
            LinkedNode<T> end = CurrentNode;
            end.Value = default(T);
            LinkedNode<T> node = CurrentNode.Next;
            while (node != end)
            {
                node.Value = default(T);
                node = node.Next;
            }
        }

        public bool Contains(T item)
        {
            EqualityComparer<T> compare = EqualityComparer<T>.Default;
            foreach (T i in this)
            {
                if (compare.Equals(i, item)) return true;
            }
            return false;
        }

        public LinkedNode<T> Find(T item)
        {
            LinkedNode<T> node = CurrentNode;
            EqualityComparer<T> compare = EqualityComparer<T>.Default;
            do
            {
                if (compare.Equals(item, node.Value)) return node;
                node = node.Next;
            } while (node != CurrentNode);
            return null;
        }

        public void CopyTo(T[] array, int index)
        {
            LinkedNode<T> node = CurrentNode;
            if (node != null)
            {
                do
                {
                    array[index++] = node.Value;
                    node = node.Next;
                } while (node != CurrentNode);
            }
        }

        public bool Remove(T item)
        {
            LinkedNode<T> node = Find(item);
            if (node != null)
            {
                node.Value = default(T);
            }
            return node != null;
        }

        private int _capacity;

        public int Count
        {
            get => _capacity;
            set
            {
                if (value < 1 || _capacity == value) return;
                if (value > _capacity)
                {
                    LinkedNode<T> start = CurrentNode.Previous;
                    for (int i = 0; i < value - _capacity; i++)
                    {
                        LinkedNode<T> node = new LinkedNode<T>(_capacity + i);
                        start.SetNext(node);
                        start = node;
                    }

                    start.SetNext(CurrentNode);
                }
                else
                {
                    LinkedNode<T> breakPoint = CurrentNode;
                    for (int i = 0; i < _capacity - value; i++)
                    {
                        breakPoint = breakPoint.Previous;
                        breakPoint.Value = default(T);
                    }

                    LinkedNode<T>.Connect(breakPoint, CurrentNode);
                }

                _capacity = value;
            }
        }

        public bool IsReadOnly => false;

        public IEnumerator<T> GetEnumerator()
        {
            return new Enumerator(this);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public struct Enumerator : IEnumerator<T>, System.Collections.IEnumerator
        {
            private readonly CircleLinkedList<T> list;
            private readonly LinkedNode<T> head;
            private LinkedNode<T> iter;
            private int counter;

            internal Enumerator(CircleLinkedList<T> list)
            {
                this.list = list;
                head = list.CurrentNode;
                iter = head.Previous;
                counter = 0;
            }

            public bool MoveNext()
            {
                iter = iter.Next;
                counter++;
                return counter <= list.Count;
            }

            public void Reset()
            {
                iter = head.Previous;
                counter = 0;
            }

            public T Current => iter.Value;

            object IEnumerator.Current => Current;

            public void Dispose()
            {
            }
        }

        #endregion
    }
}